home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / quicktime / quicktime vr / vrscript / feature files / vrsound.c < prev    next >
Encoding:
Text File  |  2000-06-23  |  26.6 KB  |  1,005 lines

  1. //////////
  2. //
  3. //    File:        VRSound.c
  4. //
  5. //    Contains:    Sound support for QuickTime VR movies.
  6. //
  7. //    Written by:    Tim Monroe
  8. //
  9. //    Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  10. //
  11. //    Change History (most recent first):
  12. //
  13. //       <11>         03/13/98    rtm        removed support for chunk-based files (AIFF and WAVE)
  14. //       <10>         09/18/97    rtm        added parameter to VRSound_CheckForCompletedSounds
  15. //       <9>         08/30/97    rtm        begun adding support for chunk-based files (AIFF and WAVE)
  16. //       <8>         08/11/97    rtm        added support for sound files (of type 'sfil')
  17. //       <7>         06/19/97    rtm        added support for scene-wide sounds
  18. //       <6>         04/30/97    rtm        added distance attenuation to VRSound_SetBalanceAndVolume
  19. //       <5>         04/25/97    rtm        added VRSound_SetBalanceAndVolume to substitute for SoundSprocket
  20. //                                    (and to support 68K Macs)
  21. //       <4>         04/24/97    rtm        merged VRSound.c with previous VR3DSound.c
  22. //       <3>         04/21/97    rtm        implemented fade-out option when stopping sounds
  23. //       <2>         04/18/97    rtm        implemented callback procedure
  24. //       <1>         04/17/97    rtm        first file
  25. //     
  26. //
  27. // ******************************************************************************
  28. // This file provides functions to play both ambient and localized sounds in QTVR
  29. // panoramas and objects.
  30. //
  31. // Some of this code is straight out of Inside Macintosh: Sound and the SoundSprocket
  32. // documentation (by yours truly!) or the SoundSprocket sample source (by Dan Venolia).
  33. // ******************************************************************************
  34. //
  35. //////////
  36.  
  37. // TODO:
  38.  
  39. // header files
  40. #include "VRSound.h"
  41. #include "QTVRUtilities.h"
  42.  
  43. // system headers
  44. #include <Resources.h>
  45. #include <CodeFragments.h>
  46.  
  47. // global variables
  48. SndCallBackUPP            gSndCallBackProc = NULL;     // a routine descriptor for our sound callback
  49. Boolean                    gHasSoundManager30;            // is Sound Manager version 3.0 (or greater) available?
  50.  
  51. extern Boolean            gHasSoundSprockets;            // is SoundSprockets available?
  52.  
  53. // constants
  54. #define kBlockIsLocked    0x80                        // the block-is-locked bit in the SignedByte returned by HGetState
  55.  
  56.  
  57. // ******************************************************************************
  58. // Sound utility functions.
  59. // ******************************************************************************
  60.  
  61. //////////
  62. //
  63. // VRSound_CheckVersionNumber
  64. // Returns true if the given version number is compatible with
  65. // (that is, not older than) version theMajor.theMinor.theBug.
  66. //
  67. //////////
  68.  
  69. Boolean VRSound_CheckVersionNumber (
  70.     const NumVersion        *theVersion,
  71.     UInt8                    theMajor,
  72.     UInt8                    theMinor,
  73.     UInt8                    theBug)
  74. {
  75.     if (theVersion->majorRev != theMajor)
  76.         return(theVersion->majorRev > theMajor);
  77.     else
  78.         return(theVersion->minorAndBugRev >= theMinor << 4 | theBug);
  79. }
  80.  
  81.  
  82. //////////
  83. //
  84. // VRSound_GetSoundHeader
  85. // Returns a pointer to the sound header in a sampled sound resource.
  86. //
  87. //////////
  88.  
  89. SoundHeaderPtr VRSound_GetSoundHeader (Handle theSndHandle)
  90. {
  91.     SoundHeaderPtr        mySndHeader = NULL;
  92.     long                myOffset = 0;
  93.     OSErr                myErr = noErr;
  94.     
  95.     myErr = GetSoundHeaderOffset((SndListHandle)theSndHandle, &myOffset);
  96.     if (myErr == noErr)
  97.         mySndHeader = (SoundHeaderPtr)(*theSndHandle + myOffset);
  98.  
  99.     return(mySndHeader);
  100. }
  101.  
  102.  
  103. //////////
  104. //
  105. // VRSound_GetSndBaseFrequency
  106. // Returns the base frequency of a sampled sound.
  107. // We need this when installing the sound as a voice, in VRSound_PlayResource.
  108. //
  109. //////////
  110.  
  111. long VRSound_GetSndBaseFrequency (Handle theSndHandle)
  112. {
  113.     SoundHeaderPtr        mySndHeader;
  114.     long                myBaseFreq = kMiddleC;        // a reasonable default
  115.     
  116.     mySndHeader = VRSound_GetSoundHeader(theSndHandle);
  117.     if (mySndHeader != NULL)
  118.         myBaseFreq = mySndHeader->baseFrequency;
  119.  
  120.     return(myBaseFreq);
  121. }
  122.  
  123.  
  124. //////////
  125. //
  126. // VRSound_GetVolume
  127. // Get the current left and right volumes of a sound channel.
  128. //
  129. //////////
  130.  
  131. OSErr VRSound_GetVolume (SndChannelPtr theChannel, unsigned short *theLeftVol, unsigned short *theRightVol)
  132. {
  133.     SndCommand            mySndCommand;
  134.     unsigned long        myVolume;
  135.     OSErr                myErr = noErr;
  136.  
  137.     if (!gHasSoundManager30)
  138.         return(paramErr);
  139.         
  140.     mySndCommand.cmd = getVolumeCmd;
  141.     mySndCommand.param1 = 0;
  142.     mySndCommand.param2 = (long)&myVolume;
  143.     myErr = SndDoImmediate(theChannel, &mySndCommand);
  144.     
  145.     if (myErr == noErr) {
  146.         *theLeftVol = myVolume & 0x0000ffff;
  147.         *theRightVol = myVolume >> 16;
  148.     }
  149.     
  150.     return(myErr);
  151. }
  152.  
  153.  
  154. //////////
  155. //
  156. // VRSound_SetVolume
  157. // Set the left and right volumes of a sound channel.
  158. //
  159. //////////
  160.  
  161. OSErr VRSound_SetVolume (SndChannelPtr theChannel, unsigned short theLeftVol, unsigned short theRightVol)
  162. {
  163.     SndCommand            mySndCommand;
  164.     OSErr                myErr = noErr;
  165.  
  166.     if (!gHasSoundManager30)
  167.         return(paramErr);
  168.         
  169.     mySndCommand.cmd = volumeCmd;
  170.     mySndCommand.param1 = 0;
  171.     mySndCommand.param2 = (theRightVol << 16) | theLeftVol;
  172.     
  173.     myErr = SndDoImmediate(theChannel, &mySndCommand);
  174.     return(myErr);
  175. }
  176.  
  177.  
  178. //////////
  179. //
  180. // VRSound_CreateSoundChannel
  181. // Create a sound channel.
  182. //
  183. //////////
  184.  
  185. SndChannelPtr VRSound_CreateSoundChannel (Boolean theSoundIsLocalized)
  186. {
  187.     SndChannelPtr        mySndChannel = NULL;
  188.     SoundComponentLink    myLink;
  189.     OSStatus            myErr;
  190.     
  191.     // create storage for a new sound channel
  192.     mySndChannel = (SndChannelPtr)NewPtrClear(sizeof(SndChannel));
  193.     if (mySndChannel == NULL)
  194.         return(mySndChannel);
  195.     
  196.     // set the number of commands in the sound channel queue
  197.     mySndChannel->qLength = kVRSound_NumCmdsInQueue;
  198.     
  199.     // create the sound channel
  200.     myErr = SndNewChannel(&mySndChannel, sampledSynth, initMono, gSndCallBackProc);
  201.     if (myErr != noErr) {
  202.         DisposePtr((Ptr)mySndChannel);
  203.         return(NULL);
  204.     }
  205.     
  206.     if (theSoundIsLocalized) {
  207.         if (gHasSoundSprockets) {
  208.             // install the 3D sound filters
  209.             myLink.description.componentType            = kSoundEffectsType;
  210.             myLink.description.componentSubType            = kSSpLocalizationSubType;
  211.             myLink.description.componentManufacturer    = 0;
  212.             myLink.description.componentFlags            = 0;
  213.             myLink.description.componentFlagsMask        = 0;
  214.             myLink.mixerID                                = NULL;
  215.             myLink.linkID                                = NULL;
  216.             
  217.             myErr = SndSetInfo(mySndChannel, siPreMixerSoundComponent, &myLink);
  218.         }
  219.     }
  220.     
  221.     if (myErr != noErr) {
  222.         DisposePtr((Ptr)mySndChannel);
  223.         mySndChannel = NULL;
  224.     }
  225.     
  226.     return(mySndChannel);
  227. }
  228.  
  229.  
  230. //////////
  231. //
  232. // VRSound_CreateLocalizedSource
  233. // Create a localized sound source.
  234. //
  235. //////////
  236.  
  237. SSpSourceReference VRSound_CreateLocalizedSource (void)
  238. {
  239.     SSpSourceReference        mySource = 0;
  240.     
  241.     // create a new sound source
  242. #if SOUNDSPROCKET_AVAIL
  243.     if (gHasSoundSprockets)
  244.         SSpSource_New(&mySource);
  245. #endif
  246.  
  247.     return(mySource);
  248. }
  249.  
  250.  
  251. //////////
  252. //
  253. // VRSound_SetLocation
  254. // Set the location of a localized sound.
  255. //
  256. //////////
  257.  
  258. void VRSound_SetLocation (WindowObject theWindowObject, UInt32 theEntryID, float theX, float theY, float theZ, UInt32 theOptions)
  259. {
  260.     VRScriptSoundPtr    myPointer;
  261.  
  262.     myPointer = (VRScriptSoundPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_Sound, theEntryID);
  263.     if (myPointer != NULL) {
  264.         if (theOptions == kVRValue_Relative) {
  265.             myPointer->fLocation.x += theX;
  266.             myPointer->fLocation.y += theY;
  267.             myPointer->fLocation.z += theZ;
  268.         } else {
  269.             myPointer->fLocation.x = theX;
  270.             myPointer->fLocation.y = theY;
  271.             myPointer->fLocation.z = theZ;
  272.         }
  273.         
  274. #if SOUNDSPROCKET_AVAIL
  275.         if (gHasSoundSprockets)
  276.             SSpSource_SetPosition(myPointer->fSource, &(myPointer->fLocation));
  277. #endif
  278.     }
  279. }
  280.  
  281.  
  282. //////////
  283. //
  284. // VRSound_GetSndResourceID
  285. // Get the resource ID of the first 'snd ' resource in the specified resource file.
  286. //
  287. //////////
  288.  
  289. short VRSound_GetSndResourceID (short theRefNum)
  290. {
  291.     Handle        myResource;
  292.     short        myResID = 0;
  293.     short        myCurRefNum;
  294.     ResType        myResType;
  295.     Str255        myResName;
  296.     
  297.     // make sure the specified file is the current resource file
  298.     myCurRefNum = CurResFile();
  299.     if (theRefNum != myCurRefNum)
  300.         UseResFile(theRefNum);
  301.     
  302.     if (theRefNum > 0) {
  303.         // find the resource ID of the single 'snd ' resource in the resource file;
  304.         // we do this by loading the resource and then getting its info.
  305.         myResource = Get1IndResource(soundListRsrc, 1);
  306.         if (myResource != NULL)
  307.             GetResInfo(myResource, &myResID, &myResType, myResName);
  308.     }
  309.     
  310.     // restore the current resource file
  311.     if (theRefNum != myCurRefNum)
  312.         UseResFile(myCurRefNum);
  313.     
  314.     return(myResID);
  315. }
  316.  
  317.  
  318. // ******************************************************************************
  319. // Sound list utility functions.
  320. // ******************************************************************************
  321.  
  322. //////////
  323. //
  324. // VRSound_Update3DSoundEnv
  325. // Update the virtual audio environment.
  326. //
  327. //////////
  328.  
  329. void VRSound_Update3DSoundEnv (WindowObject theWindowObject)
  330. {
  331. #if SOUNDSPROCKET_AVAIL
  332.     SSpLocalizationData        myData;
  333. #endif
  334.     ApplicationDataHdl        myAppData;
  335.     VRScriptSoundPtr        myPointer;
  336.  
  337.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  338.     if (myAppData == NULL)
  339.         return;
  340.     
  341.     // walk our linked list and update this node's virtual audio environment
  342.     myPointer = (VRScriptSoundPtr)(**myAppData).fListArray[kVREntry_Sound];
  343.     if (gHasSoundSprockets) {
  344. #if SOUNDSPROCKET_AVAIL
  345.         while (myPointer != NULL) {
  346.             if (myPointer->fSoundIsLocalized) {
  347.                 SSpSource_CalcLocalization(myPointer->fSource, (**myAppData).fListener, &myData);
  348.                 SndSetInfo(myPointer->fChannel, siSSpLocalization, &myData);
  349.             }
  350.             myPointer = myPointer->fNextEntry;
  351.         }
  352. #endif
  353.     } else {
  354.         // VRSound_SetBalanceAndVolume is triggered by the prescreen routine, so do an update
  355.         (**myAppData).fSoundHasChanged = true;
  356.         QTVRUpdate((**theWindowObject).fInstance, kQTVRCurrentMode);
  357.     }
  358. }
  359.  
  360.  
  361. //////////
  362. //
  363. // VRSound_SetBalanceAndVolume
  364. // Set the balance and volume attenuation of any localized sounds.
  365. //
  366. // This is a low-budget SoundSprocket replacement: we use the Sound Manager's volumeCmd 
  367. // to adjust the volume and balance of a sound channel according to the given pan angle.
  368. //
  369. //////////
  370.  
  371. void VRSound_SetBalanceAndVolume (WindowObject theWindowObject, float thePan, float theTilt)
  372. {
  373. #pragma unused(theTilt)
  374.  
  375.     ApplicationDataHdl        myAppData;
  376.     VRScriptSoundPtr        myPointer;
  377.     
  378.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  379.     if (myAppData == NULL)
  380.         return;
  381.  
  382.     // walk our linked list and set the balance and volume of any localized sounds
  383.     myPointer = (VRScriptSoundPtr)(**myAppData).fListArray[kVREntry_Sound];
  384.     while (myPointer != NULL) {
  385.         if (myPointer->fSoundIsLocalized) {
  386.             float            myPan;
  387.             float            myPanDelta;
  388.             float            myCosLimit;
  389.             float            myCosDeltaPlus, myCosDeltaMinus;
  390.             unsigned short    myLeftVol, myRightVol;
  391.             float            myDistance;
  392.             
  393.             // get the pan angle of the localized sound;
  394.             myPan = QTVRUtils_Point3DToPanAngle(myPointer->fLocation.x, myPointer->fLocation.y, myPointer->fLocation.z);
  395.                 
  396.             myPanDelta = thePan - myPan;
  397.             myCosLimit = cos(myPointer->fProjAngle);
  398.             myCosDeltaPlus = cos(myPanDelta + QTVRUtils_DegreesToRadians(kVRSound_SpeakerAngle));
  399.             myCosDeltaMinus = cos(myPanDelta - QTVRUtils_DegreesToRadians(kVRSound_SpeakerAngle));
  400.             
  401.             // inside cone of attenuation, volume scales from 1.0 (at center) to 0.0 (at cone edge)
  402.             // outside cone of attenuation, volume is 0.0;
  403.             myLeftVol = (myCosDeltaPlus >= myCosLimit) ? (kQTMaxSoundVolume * ((myCosDeltaPlus - myCosLimit) / (1 - myCosLimit))) : kNoVolume;
  404.             myRightVol = (myCosDeltaMinus >= myCosLimit) ? (kQTMaxSoundVolume * ((myCosDeltaMinus - myCosLimit) / (1 - myCosLimit))) : kNoVolume;
  405.  
  406.             // now adjust the volume for the distance of the object from the listener;
  407.             // we could use many other algorithms here....
  408.             myDistance = QTVRUtils_GetDistance(myPointer->fLocation);
  409.             if (myDistance > 1.0) {
  410.                 myLeftVol /= myDistance;
  411.                 myRightVol /= myDistance;
  412.             }
  413.             
  414.             VRSound_SetVolume(myPointer->fChannel, myLeftVol, myRightVol);
  415.         }
  416.         
  417.         myPointer = myPointer->fNextEntry;
  418.     }
  419. }
  420.     
  421.  
  422. // ******************************************************************************
  423. // Application initialization and shutdown.
  424. // ******************************************************************************
  425.  
  426. //////////
  427. //
  428. // VRSound_Init
  429. // Initialize for sound.
  430. //
  431. //////////
  432.  
  433. void VRSound_Init (void)
  434. {
  435.     NumVersion            myVersion;
  436.  
  437.     // check the Sound Manager version
  438.     myVersion = SndSoundManagerVersion();
  439.     
  440.     // for doing any sound, we require version 3.0 or later
  441.     gHasSoundManager30 = VRSound_CheckVersionNumber(&myVersion, 3, 0, 0);
  442.  
  443.     // for doing localized sound using SoundSprockets, we require version 3.2.1 or later
  444.     if (!VRSound_CheckVersionNumber(&myVersion, 3, 2, 1))
  445.         gHasSoundSprockets = false;
  446.     
  447.     // now see whether SoundSprockets is available;
  448.     // there is no Gestalt selector for this package, so we use an alternate strategy:
  449. #if SOUNDSPROCKET_AVAIL
  450.     gHasSoundSprockets = ((short)SSpListener_New != (short)kUnresolvedCFragSymbolAddress);
  451. #else
  452.     gHasSoundSprockets = false;
  453. #endif
  454.  
  455.     // allocate a routine descriptor for our sound callback routine
  456.     if (gSndCallBackProc == NULL)
  457.         gSndCallBackProc = NewSndCallBackProc(VRSound_CallbackProc);
  458. }
  459.  
  460.  
  461. //////////
  462. //
  463. // VRSound_Stop
  464. // Close down for sound.
  465. //
  466. //////////
  467.  
  468. void VRSound_Stop (void)
  469. {
  470.     // deallocate routine descriptor
  471.     if (gSndCallBackProc != NULL)
  472.         DisposeRoutineDescriptor(gSndCallBackProc);
  473. }
  474.  
  475.  
  476. // ******************************************************************************
  477. // Window-specific initialization and clean-up.
  478. // ******************************************************************************
  479.  
  480. //////////
  481. //
  482. // VRSound_InitWindowData
  483. // Initialize window-specific data for sounds.
  484. //
  485. //////////
  486.  
  487. void VRSound_InitWindowData (WindowObject theWindowObject)
  488. {
  489.     ApplicationDataHdl    myAppData;
  490.  
  491.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  492.     if (myAppData == NULL)
  493.         return;
  494.  
  495. #if SOUNDSPROCKET_AVAIL
  496.     if (gHasSoundSprockets) {
  497.         OSStatus        myErr;
  498.     
  499.         // create a listener and set listener units to feet
  500.         myErr = SSpListener_New(&(**myAppData).fListener);
  501.         if (myErr == noErr)
  502.             SSpListener_SetMetersPerUnit((**myAppData).fListener, 0.3048);
  503.     }
  504. #endif
  505. }
  506.  
  507.  
  508. //////////
  509. //
  510. // VRSound_DumpWindowData
  511. // Get rid of window-specific data for sounds.
  512. //
  513. //////////
  514.  
  515. void VRSound_DumpWindowData (WindowObject theWindowObject)
  516. {
  517.     ApplicationDataHdl    myAppData;
  518.  
  519.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  520.     if (myAppData == NULL)
  521.         return;
  522.         
  523.     VRSound_DumpNodeSounds(theWindowObject);
  524.     VRSound_DumpSceneSounds(theWindowObject);
  525.  
  526. #if SOUNDSPROCKET_AVAIL
  527.     // shut down SoundSprockets, if enabled
  528.     if (gHasSoundSprockets && ((**myAppData).fListener != NULL)) 
  529.         SSpListener_Dispose((**myAppData).fListener);
  530. #endif
  531. }
  532.  
  533.  
  534. // ******************************************************************************
  535. // Manipulating multiple sound channels.
  536. // ******************************************************************************
  537.  
  538. //////////
  539. //
  540. // VRSound_DumpNodeSounds
  541. // Stop playing all sounds and release all sound resources enlisted for the current node.
  542. //
  543. //////////
  544.  
  545. void VRSound_DumpNodeSounds (WindowObject theWindowObject)
  546. {
  547.     VRSound_DumpSelectedSounds(theWindowObject, kVRSelect_Node);
  548. }
  549.  
  550.  
  551. //////////
  552. //
  553. // VRSound_DumpSceneSounds
  554. // Stop playing all sounds and release all sound resources enlisted for the current scene.
  555. //
  556. //////////
  557.  
  558. void VRSound_DumpSceneSounds (WindowObject theWindowObject)
  559. {
  560.     VRSound_DumpSelectedSounds(theWindowObject, kVRSelect_Scene);
  561. }
  562.  
  563.  
  564. //////////
  565. //
  566. // VRSound_DumpSelectedSounds
  567. // Stop playing selected sounds and release selected sound resources.
  568. //
  569. //////////
  570.  
  571. void VRSound_DumpSelectedSounds (WindowObject theWindowObject, UInt32 theOptions)
  572. {
  573.     ApplicationDataHdl    myAppData;
  574.     VRScriptSoundPtr    myPointer;
  575.     VRScriptSoundPtr    myNext;
  576.     
  577.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  578.     if (myAppData == NULL)
  579.         return;
  580.  
  581.     // stop playing and delist any sounds associated with this node
  582.     myPointer = (VRScriptSoundPtr)(**myAppData).fListArray[kVREntry_Sound];
  583.     while (myPointer != NULL) {        
  584.         myNext = myPointer->fNextEntry;
  585.         if (((myPointer->fNodeID != kVRAnyNode) && (theOptions == kVRSelect_Node)) ||
  586.             ((myPointer->fNodeID == kVRAnyNode) && (theOptions == kVRSelect_Scene))) {
  587.             if (myPointer->fFadeWhenStopping)
  588.                 VRSound_FadeSilence(theWindowObject, myPointer->fChannel);
  589.             else
  590.                 VRSound_PlaySilence(theWindowObject, myPointer->fChannel);
  591.                 
  592.             VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
  593.         }
  594.         myPointer = myNext;
  595.     }
  596. }
  597.  
  598.  
  599. // ******************************************************************************
  600. // Manipulating a single sound channel.
  601. // ******************************************************************************
  602.  
  603. //////////
  604. //
  605. // VRSound_FadeSilence
  606. // Fade out the sound playing on a particular sound channel.
  607. //
  608. //////////
  609.  
  610. void VRSound_FadeSilence (WindowObject theWindowObject, SndChannelPtr theChannel)
  611. {
  612. #pragma unused(theWindowObject)
  613.  
  614.     unsigned short        myLeftVol;
  615.     unsigned short        myRightVol;
  616.     short                myCount;
  617.     unsigned long        myTicks;
  618.     float                myFactor;
  619.     OSErr                myErr = noErr;
  620.  
  621.     if (theChannel == NULL)
  622.         return;
  623.     
  624.     // get the current right and left volumes
  625.     myErr = VRSound_GetVolume(theChannel, &myLeftVol, &myRightVol);
  626.     
  627.     if (myErr == noErr) {
  628.         // now gradually reduce the volume of each channel to silence
  629.         for (myCount = 1; myCount < kVRSound_NumFadeSteps; myCount++) {
  630.             Delay(kVRSound_FadeStepWait, &myTicks);            // wait a small bit, to make fade perceptible
  631.             myFactor = (float)(kVRSound_NumFadeSteps - myCount) / (float)kVRSound_NumFadeSteps;
  632.             myLeftVol *= myFactor;
  633.             myRightVol *= myFactor;
  634.             VRSound_SetVolume(theChannel, myLeftVol, myRightVol);
  635.         }
  636.     }    
  637. }
  638.  
  639.  
  640. //////////
  641. //
  642. // VRSound_PlaySilence
  643. // Stop the sound playing on a particular sound channel.
  644. //
  645. //////////
  646.  
  647. void VRSound_PlaySilence (WindowObject theWindowObject, SndChannelPtr theChannel)
  648. {
  649. #pragma unused(theWindowObject)
  650.     SndCommand            mySndCommand;
  651.  
  652.     if (theChannel == NULL)
  653.         return;
  654.         
  655.     mySndCommand.cmd = quietCmd;
  656.     mySndCommand.param1 = 0;
  657.     mySndCommand.param2 = 0;
  658.     SndDoImmediate(theChannel, &mySndCommand);
  659. }
  660.  
  661.  
  662. //////////
  663. //
  664. // VRSound_PlayResource
  665. // Play the specified sound resource on the specified sound channel.
  666. //
  667. //////////
  668.  
  669. void VRSound_PlayResource (WindowObject theWindowObject, SndChannelPtr theChannel, SndListHandle theResHandle, UInt32 theOptions)
  670. {
  671.     SndCommand            mySndCommand;
  672.     long                myOffset;
  673.     ApplicationDataHdl    myAppData;
  674.     SignedByte            myHState;
  675.     OSErr                myErr = noErr;
  676.  
  677.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  678.     if (myAppData == NULL)
  679.         return;
  680.         
  681.     if (theResHandle == NULL)
  682.         return;
  683.  
  684.     // silence the sound channel
  685.     VRSound_PlaySilence(theWindowObject, theChannel);
  686.         
  687.     // lock the resource data down, if it isn't already locked
  688.     myHState = HGetState((Handle)theResHandle);
  689.     if (!(myHState & kBlockIsLocked))
  690.         HLock((Handle)theResHandle);
  691.     
  692.     GetSoundHeaderOffset(theResHandle, &myOffset);
  693.     
  694.     switch (theOptions) {
  695.         case kVRPlay_Loop:
  696.             // loop the sound indefinitely
  697.             mySndCommand.cmd = soundCmd;
  698.             mySndCommand.param1 = 0;
  699.             mySndCommand.param2 = (long)*(theResHandle) + myOffset;
  700.             myErr = SndDoImmediate(theChannel, &mySndCommand);
  701.  
  702.             mySndCommand.cmd = freqCmd;
  703.             mySndCommand.param1 = 0;
  704.             mySndCommand.param2 = VRSound_GetSndBaseFrequency((Handle)theResHandle);
  705.             myErr = SndDoImmediate(theChannel, &mySndCommand);
  706.             break;
  707.             
  708.         case kVRPlay_Once:
  709.             // play the sound once thru, asynchronously
  710.             mySndCommand.cmd = bufferCmd;
  711.             mySndCommand.param1 = 0;
  712.             mySndCommand.param2 = (long)*(theResHandle) + myOffset;
  713.             myErr = SndDoImmediate(theChannel, &mySndCommand);
  714.             break;
  715.         
  716.         default:
  717.             break;
  718.     }
  719. }
  720.  
  721.  
  722. //////////
  723. //
  724. // VRSound_PlaySound
  725. // Start a sound playing or stop one from playing.
  726. //
  727. //////////
  728.  
  729. void VRSound_PlaySound (WindowObject theWindowObject, UInt32 theNodeID, UInt32 theResID, UInt32 theEntryID, float theX, float theY, float theZ, float theProjAngle, UInt32 theSourceMode, UInt32 theMode, UInt32 theFade, UInt32 theOptions)
  730. {
  731.     ApplicationDataHdl        myAppData;
  732.     Handle                    myHandle;
  733.     Boolean                    myNeedPlaySound = false;
  734.     Boolean                    myNeedNewChannel = false;
  735.     SndChannelPtr            mySndChannel;
  736.     VRScriptSoundPtr        myPointer = NULL;
  737.     SSpSourceReference        mySource = 0;
  738.     
  739.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
  740.     if (myAppData == NULL)
  741.         return;
  742.     
  743.     // see if this sound is already in our list of playing sounds                
  744.     myPointer = (VRScriptSoundPtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_Sound, theEntryID);
  745.     if (myPointer == NULL) {
  746.         // this sound isn't in our list yet, so we'll need to add it to the list
  747.         myNeedPlaySound = true;
  748.         myNeedNewChannel = true;
  749.     } else {
  750.         // this sound is already in our list; theOptions determines how we handle this request:
  751.         
  752.         mySource = myPointer->fSource;
  753.  
  754.         switch (theOptions) {
  755.         
  756.             case kVRMedia_PlayNew:
  757.                 // start the sound playing (whether it's already playing or not)
  758.                 myNeedPlaySound = true;
  759.                 myNeedNewChannel = true;
  760.                 break;
  761.                 
  762.             case kVRMedia_Restart:
  763.                 // stop the current sound and then restart it; use the existing sound channel
  764.                 // (PlayResource automatically stops the current sound on this channel)
  765.                 myNeedPlaySound = true;
  766.                 myNeedNewChannel = false;
  767.                 break;
  768.                 
  769.             case kVRMedia_ToggleStop:
  770.             case kVRMedia_TogglePause:
  771.                 // toggle the sound's current play/stop state
  772.                 myNeedNewChannel = false;
  773.                 if (myPointer->fSoundIsPlaying) {
  774.                     // stop the sound
  775.                     if (myPointer->fFadeWhenStopping)
  776.                         VRSound_FadeSilence(theWindowObject, myPointer->fChannel);
  777.                     else
  778.                         VRSound_PlaySilence(theWindowObject, myPointer->fChannel);
  779.                     myPointer->fSoundIsPlaying = false;
  780.                     myNeedPlaySound = false;
  781.                 } else {
  782.                     // start the sound; use the existing sound channel
  783.                     myNeedPlaySound = true;
  784.                 }
  785.                 break;
  786.                 
  787.             case kVRMedia_Continue:
  788.                 // just let the sound already in progress continue
  789.                 myNeedPlaySound = false;
  790.                 myNeedNewChannel = false;
  791.                 break;
  792.                 
  793.             case kVRMedia_Stop:
  794.                 // stop the sound
  795.                 if (myPointer->fFadeWhenStopping)
  796.                     VRSound_FadeSilence(theWindowObject, myPointer->fChannel);
  797.                 else
  798.                     VRSound_PlaySilence(theWindowObject, myPointer->fChannel);
  799.                 myPointer->fSoundIsPlaying = false;
  800.                 myNeedPlaySound = false;
  801.                 myNeedNewChannel = false;
  802.                 break;
  803.                 
  804.             default:
  805.                 // unrecognized option
  806.                 // start the specified resource playing; use the existing sound channel
  807.                 myNeedPlaySound = true;
  808.                 myNeedNewChannel = false;
  809.                 break;
  810.         }
  811.         
  812.     }
  813.     
  814.     if (myNeedNewChannel) {
  815.         if (theSourceMode == kSSpSourceMode_Ambient) {
  816.             mySndChannel = VRSound_CreateSoundChannel(false);
  817.         } else {
  818.             mySource = VRSound_CreateLocalizedSource();
  819.             mySndChannel = VRSound_CreateSoundChannel(true);
  820.         }
  821.         
  822.         if (mySndChannel == NULL) 
  823.             return;        // couldn't allocate a new channel, so we cannot play sound....
  824.         
  825.         myHandle = GetResource(soundListRsrc, theResID);
  826.         if (myHandle != NULL) {
  827.             HNoPurge((Handle)myHandle);
  828.             if (theSourceMode == kSSpSourceMode_Ambient) {
  829.                 myPointer = VRScript_EnlistSound(theWindowObject, theNodeID, theResID, theEntryID, theMode, theFade, theOptions, mySndChannel, (SndListHandle)myHandle);
  830.             } else {
  831.                 myPointer = VRScript_EnlistLocalizedSound(theWindowObject, theNodeID, theResID, theEntryID, theX, theY, theZ, theProjAngle, theMode, theFade, theOptions, mySndChannel, (SndListHandle)myHandle, mySource);
  832.             }
  833.         } else {
  834.             SndDisposeChannel(mySndChannel, true);
  835.             DisposePtr((Ptr)mySndChannel);
  836.             return;
  837.         }
  838.         
  839.     }
  840.  
  841.     if (myNeedPlaySound) {
  842.         if (myPointer != NULL) {
  843.         
  844.             if (theSourceMode != kSSpSourceMode_Ambient) {
  845.                 if (gHasSoundSprockets) {
  846.                     TQ3Point3D            myPoint;
  847.                     TQ3Vector3D            myOrient;
  848.                     
  849.                     // install this sound using SoundSprocket's high-level interfaces
  850.                     myPoint.x = theX;
  851.                     myPoint.y = theY;
  852.                     myPoint.z = theZ;
  853.                     
  854.                     myOrient.x = -myPoint.x;
  855.                     myOrient.y = -myPoint.y;
  856.                     myOrient.z = -myPoint.z;
  857.                     
  858. #if SOUNDSPROCKET_AVAIL
  859.                     SSpSource_SetPosition(mySource, &myPoint);
  860.                     SSpSource_SetOrientation(mySource, &myOrient);
  861.                     SSpSource_SetAngularAttenuation(mySource, kVRPi/8, 20.0);
  862.                     SSpSource_SetMode(mySource, theSourceMode);
  863.                     SSpSource_SetReferenceDistance(mySource, 1.0);
  864.                     SSpSource_SetSize(mySource, 0.0, 0.0, 0.0);
  865. #endif
  866.                 }
  867.                 
  868.                 VRSound_Update3DSoundEnv(theWindowObject);
  869.             }
  870.             
  871.             VRSound_PlayResource(theWindowObject, myPointer->fChannel, myPointer->fResourceData, theMode);
  872.             myPointer->fSoundIsPlaying = true;
  873.             if (theMode == kVRPlay_Once)
  874.                 VRSound_InstallCallbackMessage(myPointer, kVRSound_Complete);
  875.         }    
  876.     }
  877. }
  878.  
  879.  
  880. // ******************************************************************************
  881. // Callback procedure functions and utilities.
  882. // ******************************************************************************
  883.  
  884. //////////
  885. //
  886. // VRSound_InstallCallbackMessage
  887. // Clean up after a sound is finished playing.
  888. //
  889. //////////
  890.  
  891. void VRSound_InstallCallbackMessage (VRScriptSoundPtr theEntry, short theMessage)
  892. {
  893.     if (theEntry != NULL) {
  894.         SndCommand            myCommand;
  895.         
  896.         myCommand.cmd = callBackCmd;
  897.         myCommand.param1 = theMessage;
  898.         myCommand.param2 = (long)theEntry;
  899.         SndDoCommand(theEntry->fChannel, &myCommand, false);
  900.     }
  901. }
  902.  
  903.  
  904. //////////
  905. //
  906. // VRSound_CallbackProc
  907. // Handle callback messages.
  908. //
  909. //////////
  910.  
  911. PASCAL_RTN void VRSound_CallbackProc (SndChannelPtr theChannel, SndCommand *theCommand)
  912. {
  913. #pragma unused(theChannel)
  914.  
  915.     VRScriptSoundPtr    myPointer = NULL;
  916.     
  917.     switch(theCommand->param1) {
  918.         case kVRSound_Complete:
  919.             myPointer = (VRScriptSoundPtr)(theCommand->param2);
  920.             if (myPointer != NULL)
  921.                 myPointer->fSoundIsPlaying = false;
  922.             break;
  923.             
  924.         default:
  925.             break;
  926.     }
  927.  
  928. }
  929.  
  930.  
  931. //////////
  932. //
  933. // VRSound_GetFinishedSound
  934. // Get the first enlisted sound that is done playing.
  935. //
  936. //////////
  937.  
  938. VRScriptSoundPtr VRSound_GetFinishedSound (WindowObject theWindowObject)
  939. {
  940.     ApplicationDataHdl    myAppData;
  941.     VRScriptSoundPtr    myPointer = NULL;
  942.  
  943.     myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);    
  944.     if (myAppData == NULL)
  945.         return(NULL);
  946.     
  947.     // walk our linked list of sounds to find the target sound
  948.     myPointer = (VRScriptSoundPtr)(**myAppData).fListArray[kVREntry_Sound];
  949.     while (myPointer != NULL) {
  950.         if (myPointer->fSoundIsPlaying == false)
  951.             return(myPointer);
  952.  
  953.         myPointer = myPointer->fNextEntry;
  954.     }
  955.     
  956.     return(NULL);
  957. }
  958.  
  959.  
  960. //////////
  961. //
  962. // VRSound_CheckForCompletedSounds
  963. // Clean up any sounds that are finished playing.
  964. //
  965. //////////
  966.  
  967. void VRSound_CheckForCompletedSounds (WindowObject theWindowObject)
  968. {
  969.     VRScriptSoundPtr        myPointer = NULL;
  970.  
  971.     // delist any completed sounds for the specified movie window
  972.     if (theWindowObject != NULL)
  973.         while ((myPointer = VRSound_GetFinishedSound(theWindowObject)) != NULL)
  974.             VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
  975. }
  976.  
  977.  
  978. //////////
  979. //
  980. // VRSound_DumpEntryMem
  981. // Release any memory associated with the specified list entry.
  982. //
  983. //////////
  984.  
  985. void VRSound_DumpEntryMem (VRScriptSoundPtr theEntry)
  986. {
  987.     if (theEntry == NULL)
  988.         return;
  989.  
  990.     // how we release memory depends on the original container of the sound data
  991.     if (theEntry->fSoundContainer == kVRSound_SoundResource) {
  992.         HUnlock((Handle)(theEntry->fResourceData));
  993.         ReleaseResource((Handle)(theEntry->fResourceData));
  994.     }
  995.     
  996. #if SOUNDSPROCKET_AVAIL
  997.     if (gHasSoundSprockets && (theEntry->fSource != NULL))
  998.         SSpSource_Dispose(theEntry->fSource);
  999. #endif
  1000.  
  1001.     SndDisposeChannel(theEntry->fChannel, true);
  1002.     DisposePtr((Ptr)theEntry->fChannel);            // because we allocated the storage ourselves....
  1003. }
  1004.  
  1005.